Apple, the Apple logo, and Macintosh are registered trademarks of Apple Computer, Inc.
Mac and OpenDoc are trademarks of Apple Computer, Inc.
Changes since DR2
1) Refcounting methods name change.
Why do we need ODRefCntObject?
During an OpenDoc session, many objects are created. Since there is a very complex relationshiop among objects, it is difficult to determine when it is safe to delete an object.
Ref-counting is a way to determine when these runtime objects can be deleted so that valuable memory space can be reclaimed.
What is ODRefCntObject?
ODRefCntObject is an object with a refCount. A refCount must be 0 or a positive
1) Every ODRefCntObject constructed has a RefCount of 1.
2) Every ODRefCntObject is "created" or "acquired" from an object which keeps
track of it (a.k.a. factory object).
ODRefCntObject Factory object
ODLink ODDraft
ODLinkSource ODDraft
ODPart ODDraft
ODFrame ODDraft
ODContainer ODStorageSystem
ODDocument ODContainer
ODDraft ODDocument
ODStorageUnit ODDraft
ODWindow ODWindowState
ODExtension ODObject (or subclass)
ODShape ODFrame
ODTransform ODFrame
For example, to get a ODFrame object, one has to call ODDraft's CreateFrame or
ODDraft's AcquireFrame. The ODFrame object returned from ODDraft's CreateFrame will have a RefCount of 1 while the ODFrame object returned from ODDraft's AcquireFrame will have a RefCount of at least 1.
Let's look at a very simple example:
ODFrame* frame1 = draft->CreateFrame(…); // frame1's RefCount is 1
ODFrame* frame2 = draft->AcquireFrame(…); // frame2 == frame1 and
// its RefCount is 2
frame2->Release(); // frame2's RefCount is 1.
// Since frame1 == frame2,
// frame1's RefCount is also 1.
// The user should not use frame2 after this point!
frame1->Acquire(…); // frame1's RefCount is 2
frame1->Release(…); // frame1's RefCount is 1
frame1->Release(…); // frame1's RefCount is 0
// The user should not use frame1 after this point!
frame1->Acquire(…); or // ERROR!!!!!!!
frame1->Release(…); // ERROR!!!!!!!
3) When the RefCount goes down to 0, it is the object's responsibility to tell the
factory object about it.
For example, ODFrame's Release() can be implemented like this:
parent_Release(somSelf, ev); // which calls ODRefCntObject's Release()
if (somSelf->GetRefCount(ev) == 0)
fDraft->ReleaseFrame(ev, somSelf); // Call the factory object
}
The factory object can choose to dispose of the object immediately or keep the object around until a purge is called upon itself.
In the OpenDoc Macintosh implementation, ODDraft uses the latter strategy. Therefore, ODDraft will not delete the object as soon as ODDraft's ReleaseXXX is called. Instead, it will put this released object in a collection so that it can be retrieved and reused again (i.e., when ODDraft's AcquireXXX is called for that object). But if ODDraft's Purge() is called, all the released objects in the collection will be deleted.
The reason why the object is not deleted immediately is that the object may be reused in the near future. One good example is scrolling. Parts may choose to release objects that are scrolled out of view and re-get them when they come into view again. If we delete the objects as soon as ReleaseXXX is called, we will have to reinstantiate these objects again. Creating persistent objects is not a fast process as internalization will require accessing the secondary storage (namely, the disk).
4) Every time a reference to a ODRefCntObject (i.e., ODRefCntObject pointer) is cached in some data structure, ODRefCntObj->Acquire() should be called by the code that manipulates this data structure. When the code is finished with the ODRefCntObj reference in the data structure, it should call ODRefCntObj->Release().
For example, since ODFrame is keeping a reference to the containing frame and the part, it has to increment the refCounts on both of these objects.
void ODFrameInitFrame(ODFrame* somSelf,
Environment* ev,
ODStorageUnit* storageUnit,
ODFrame* containingFrame,
ODShape* frameShape,
ODPart* part,
ODTypeToken viewType,
ODTypeToken presentation,
ODULong frameGroup,
ODBoolean isRoot,
ODBoolean isOverlaid)
{
......
_fContainingFrame = containingFrame;
if (_fContainingFrame != kODNULL)
_fContainingFrame->Acquire(ev);
_fPart = part;
if (_fPart != kODNULL)
_fPart->Acquire(ev);
......
}
In ODFrame's ReleaseAll(), the frame has to release the containing frame and the part. Otherwise, the containing frame and the part will always have a RefCount greater than 0 and they will never be disposed of.
Note that the containing frame and the part may have RefCounts greater than 0 even after ODFrame's ReleaseAll() is called. This is because other objects may be keeping a reference to the containing frame or the part.
What are the implications on ODPersistentObjects?
When the RefCount of a persistent object goes from 1 to 0, the part has to call its
factory object (i.e. ODDraft). The code looks something like this:
parent_Release(ev); // which calls ODRefCntObject's Release(ev).
if (somSelf->GetRefCount(ev) == 0)
_fDraft->ReleasePart(ev, somSelf);
}
As mentioned above, ODDraft does not delete the released object immediately. Therefore, a part does not need to do any shutting down or deallocation when its RefCount goes down to 0. They will then be done in in ReleaseAll() and the destructor (i.e., somUninit).
One should not consider a persistent object with a zero refcount a dead object because at any point the draft may increment its refcount and hand it to some other objects. Having a refcount of 0 simply means no one has a reference to the object.
However, if a part needs to function differently when there is no reference to it, it can use the RefCount to identify the situation and respond appropriately. For example, it may also choose to get rid of any structure or service that is not needed anymore. For example, a Communication part may choose to close its driver when there is no reference to it.
If the object does something other than calling its factory object when its RefCount goes down to 0, it needs to "reinitialize" itself when its RefCount is bumped from 0 to 1. This is being done in the object's Acquire() call. In general, the object should undo what it did in Release() when the RefCount went
down to 0.
The following is an example of what the above-mentioned CommPart would do in response to its RefCount going from 0 to 1.